#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <cstdint>
#include <unordered_map>
#include <fstream>
#include <mutex>

#include "util.hpp"

namespace ns_index
{

    struct Doc_Info
    {
        std::string title; // 文档标题
        std::string body;  // 文档内容
        std::string url;   // 文档url

        uint64_t doc_id; // 文档id
    };

    struct InvertedElem
    {
        uint64_t doc_id;
        std::string world;

        int weight;
    };

    // 创建倒排拉链
    typedef std::vector<InvertedElem> InvertedList;

    class Index
    {

        // 创建正排索引,数组下表对应相应的文档id
        std::vector<Doc_Info> index_forward;

        // 创建倒排索引

        std::unordered_map<std::string, InvertedList> index_inverted;

    private:
        Index() {}

        // 将拷贝构造和赋值构造删除
        Index(const Index &) = delete;
        Index &operator=(const Index &) = delete;
        public:

        static Index *instence;
        static std::mutex lock;

    public:
        ~Index() {}

    public:
        // 提供获取类的单例函数
        static Index *GetIndex()
        {
            if (nullptr == instence)
            {
                lock.lock();

                if (nullptr == instence)
                {
                    instence = new Index();
                }
                lock.unlock();
            }
            return instence;
        }

        
        // 根据doc_id找到文档内容
        Doc_Info *GetForwardIndex(uint64_t doc_id)
        {
            if (doc_id >= index_forward.size())
            {
                std::cerr << "doc_id is error" << std::endl;
                return nullptr;
            }

            return &index_forward[doc_id];
        }

        // 根据关键字，获得倒排拉链

        InvertedList *GetInvertedIndex(const std::string &world)
        {
            auto it = index_inverted.find(world);

            if (it == index_inverted.end())
            {
                std::cerr << " world not exists " << std::endl;
                return nullptr;
            }

            return &(it->second);
        }

        // 根据去标签格式化后的文档，构建正排索引和倒排索引

        bool BuildIndex(const std::string &input)
        {
            // 读取文件内容

            std::ifstream fst(input, std::ios::binary | std::ios::in);

            if (!fst.is_open())
            {
                std::cerr << input << " open error" << std::endl;
                return false;
            }

            std::string line;

            uint64_t count=0;

            while (std::getline(fst, line))
            {
                
                Doc_Info *doc = BulidForwardIndex(line);

                if (doc == nullptr)
                {
                    std::cerr << line << " build error";
                    return false;
                }

                // 根据正排索引创建倒排索引
                BuildInvertedIndex(*doc);

                count++;
                if(count%50==0)
                {
                    std::cout<<"创建索引个数："<<count<<std::endl;
                }
                
            }

            return true;
        }

    private:
        Doc_Info *BulidForwardIndex(const std::string line)
        {
            // 字符串分割

            std::vector<std::string> results;

            const std::string sep = "\3";
            util::StringUtil::Split(line, &results, sep);
            // 数据填充
            if (results.size() != 3)
            {
                std::cerr << "results error" << std::endl;
                return nullptr;
            }

            Doc_Info info;
            info.title = results[0];
            info.body = results[1];
            info.url = results[2];

            info.doc_id = index_forward.size();
            // 加入到正排索引

            index_forward.push_back(std::move(info));

            return &(index_forward.back());
        }

        void BuildInvertedIndex(Doc_Info doc)
        {
            // 对字符串进行分词
            struct world_cnt
            {
                int title_cnt;
                int body_cnt;

                world_cnt() : title_cnt(0), body_cnt(0) {}
            };

            // 对单个html的title和body分别进行词频统计

            std::unordered_map<std::string, world_cnt> world_map;

            // 对title进行拆分
            std::vector<std::string> title_world;
            util::JiebaUtil::CutString(doc.title, &title_world);

            // 统计title词频
            for (auto &s : title_world)
            {
                boost::to_lower(s);
                world_map[s].title_cnt++;
            }

            // 对body进行拆分
            std::vector<std::string> body_world;
            util::JiebaUtil::CutString(doc.body, &body_world);

            // 统计body词频
            for (auto &s : body_world)
            {
                boost::to_lower(s);
                world_map[s].body_cnt++;
            }

            // 将统计后的词按照一定的权重比填入

#define X 1
#define Y 10

            for (auto &world_pair : world_map)
            {
                InvertedElem item;
                item.doc_id = doc.doc_id;
                item.world = world_pair.first;
                item.weight = world_pair.second.body_cnt * X + world_pair.second.title_cnt * Y;

                // 最后倒排索引
                index_inverted[world_pair.first].push_back(std::move(item));
            }
        }
    };

    Index* Index::instence =nullptr;
    std::mutex Index::lock;

}
